package com.bpmn.flow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.bpmn.common.constant.BpmnModelConstants;
import com.bpmn.common.enums.ProcessModelStatus;
import com.bpmn.common.exception.ServiceException;
import com.bpmn.common.holder.TenantContextHolder;
import com.bpmn.common.pojo.PageResult;
import com.bpmn.common.utils.MapUtils;
import com.bpmn.flow.controller.form.ModelForm;
import com.bpmn.flow.controller.form.ModelMetaInfoForm;
import com.bpmn.flow.controller.query.FlowModelQuery;
import com.bpmn.flow.controller.vo.ModelVo;
import com.bpmn.flow.service.DefinitionService;
import com.bpmn.flow.service.ModelService;
import com.bpmn.utils.ModelUtils;
import com.bpmn.utils.QueryUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.validation.ValidationError;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author rain
 */
@Slf4j
@Service
public class ModelServiceImpl implements ModelService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private DefinitionService definitionService;

    /**
     * 根据查询条件分页获取流程模型
     *
     * @param flowModelQuery 查询条件封装对象，包含租户ID、模型名称、模型键和模型类别
     * @return 返回包含模型列表和总记录数的分页结果对象
     */
    @Override
    public PageResult<ModelVo> getFlowModels(FlowModelQuery flowModelQuery) {
        // 创建模型查询对象
        ModelQuery query = repositoryService.createModelQuery();
        query.modelTenantId(TenantContextHolder.getTenantId());
        // 根据模型名称模糊查询，如果模型名称不为空
        if (StrUtil.isNotBlank(flowModelQuery.getName())) {
            query.modelNameLike("%" + flowModelQuery.getName() + "%");
        }
        // 根据模型键查询，如果模型键不为空
        if (StrUtil.isNotBlank(flowModelQuery.getKey())) {
            query.modelKey(flowModelQuery.getKey());
        }
        // 根据模型类别查询，如果模型类别不为空
        if (StrUtil.isNotBlank(flowModelQuery.getCategory())) {
            query.modelCategory(flowModelQuery.getCategory());
        }
        // 按最后更新时间降序排列
        query.orderByLastUpdateTime().desc();
        // 创建时间降序排列
        query.orderByCreateTime().desc();
        // 执行分页查询
        List<Model> modelList = query.listPage(flowModelQuery.getFirstNum(), flowModelQuery.getPageSize());
        // 遍历模型列表，为每个模型设置流程定义ID和状态
        List<ModelVo> modelVoList = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(modelList)) {
            Set<String> deploymentIds = CollStreamUtil.toSet(modelList, Model::getDeploymentId);
            // 获取流程定义列表，根据模型ID获取对应的流程定义列表
            List<ProcessDefinition> processDefinitionList = definitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
            List<Deployment> deploymentList = repositoryService.createDeploymentQuery().deploymentIds(new ArrayList<>(deploymentIds)).list();
            Map<String, ProcessDefinition> processDefinitionMap = CollStreamUtil.toIdentityMap(processDefinitionList, ProcessDefinition::getDeploymentId);
            Map<String, Deployment> deploymentMap = CollStreamUtil.toIdentityMap(deploymentList, Deployment::getId);
            // 遍历模型列表，为每个模型设置流程定义ID和状态
            for (Model model : modelList) {
                ModelVo modelVo = BeanUtil.toBean(model, ModelVo.class);
                modelVo.setStatus(ProcessModelStatus.DRAFT.getLowerCaseStatus());
                MapUtils.findAndThen(processDefinitionMap, modelVo.getDeploymentId(), processDefinition -> {
                    modelVo.setProcessDefinitionId(processDefinition.getId());
                    modelVo.setStatus(processDefinition.isSuspended() ? ProcessModelStatus.INVALID.getLowerCaseStatus()
                            : ProcessModelStatus.DEPLOYED.getLowerCaseStatus());
                });
                MapUtils.findAndThen(deploymentMap, modelVo.getDeploymentId(), deployment -> modelVo.setDeploymentTime(deployment.getDeploymentTime()));
                ModelMetaInfoForm metaInfo = JSONUtil.toBean(model.getMetaInfo(), ModelMetaInfoForm.class);
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setUserFetchUrl(metaInfo.getUserFetchUrl());
                modelVo.setTaskCallbackUrl(metaInfo.getTaskCallbackUrl());
                modelVoList.add(modelVo);
            }
        }
        // 返回分页结果对象，包含查询到的模型列表和总记录数
        return new PageResult<>(modelVoList, query.count());
    }

    /**
     * 创建流程模型
     *
     * @param modelForm 流程模型视图对象，包含了需要创建的流程模型的基本信息
     * @return boolean 表示流程模型是否成功创建并保存 默认返回false，因为实际的保存逻辑未在本方法中实现
     */
    @Override
    public boolean createFlowModel(ModelForm modelForm) {
        try {
            Model checkModel = QueryUtils.modelQuery().modelKey(modelForm.getKey()).singleResult();
            if (ObjectUtil.isNotNull(checkModel)) {
                throw new ServiceException("模型key已存在！");
            }
            // 初始化模型版本为0，表示这是一个新创建的模型
            int version = 0;
            // 通过repositoryService创建一个新的模型实例
            Model model = repositoryService.newModel();
            // 设置模型的关键字，用于唯一标识模型
            model.setKey(modelForm.getKey());
            // 设置模型的名称
            model.setName(modelForm.getName());
            // 设置模型的版本
            model.setVersion(version);
            // 设置模型的分类
            model.setCategory(modelForm.getCategory());
            // 设置模型的元信息，这里使用描述信息作为元信息
            model.setMetaInfo(JSONUtil.toJsonStr(BeanUtil.toBean(modelForm, ModelMetaInfoForm.class)));
            // 设置模型的租户ID
            model.setTenantId(TenantContextHolder.getTenantId());
            // 保存模型到模型仓库中
            repositoryService.saveModel(model);
            log.info("Flow model created successfully with key: {}", modelForm.getKey());
            return true;
        } catch (Exception e) {
            log.error("Failed to create flow model: {}", e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 更新流程模型
     *
     * @param modelForm 流程模型的视图对象，包含需要更新的模型信息
     * @return boolean 表示更新操作是否成功如果返回true，则表示更新成功；如果返回false，则表示更新失败
     */
    @Override
    public boolean updateFlowModel(ModelForm modelForm) {
        List<Model> list = QueryUtils.modelQuery().modelKey(modelForm.getKey()).list();
        list.stream().filter(e -> !e.getId().equals(modelForm.getId())).findFirst().ifPresent(e -> {
            throw new ServiceException("模型KEY已存在！");
        });
        try {
            // 尝试从仓库服务中获取指定ID的模型
            Model model = repositoryService.getModel(modelForm.getId());
            // 更新模型的分类和元信息
            model.setCategory(modelForm.getCategory());
            model.setMetaInfo(JSONUtil.toJsonStr(BeanUtil.toBean(modelForm, ModelMetaInfoForm.class)));
            // 保存更新后的模型
            repositoryService.saveModel(model);
            return true;
        } catch (Exception e) {
            // 记录异常日志
            log.error("Failed to update flow model: {}", e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 设计流程模型
     *
     * @param id  模型ID
     * @param xml 模型XML
     * @return boolean 表示设计操作是否成功如果返回true，则表示设计成功；如果返回false，则表示设计失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean modelDesign(String id, String xml) {
        try {
            Model model = repositoryService.getModel(id);
            if (model == null) {
                log.warn("Model with id {} not found", id);
                return false;
            }
            // 将XML转换为BpmnModel对象
            BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
            // 检查BpmnModel对象
            ModelUtils.checkBpmnModel(bpmnModel);
            repositoryService.saveModel(model);
            repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 根据模型ID获取流程模型的详细信息
     *
     * @param id 模型的唯一标识符
     * @return ModelVo对象，包含模型的详细信息如果模型不存在或发生错误，则返回空对象
     */
    @Override
    public ModelVo getFlowModelById(String id) {
        ModelVo modelVo = new ModelVo();
        Model model = repositoryService.getModel(id);
        if (model != null) {
            try {
                byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
                if (modelEditorSource != null) {
                    // 将模型编辑源转换为UTF-8编码的字符串，并设置到ModelVo对象中
                    modelVo.setXml(StrUtil.utf8Str(modelEditorSource));
                } else {
                    // 如果模型编辑源为空，则记录警告日志
                    log.warn("Model editor source is null for model ID: {}", model.getId());
                }
                // 以下代码存在重复设置xml的逻辑错误，应删除或修正
                modelVo.setXml(StrUtil.utf8Str(modelEditorSource));
                // 将模型的基本信息设置到ModelVo对象中
                modelVo.setId(model.getId());
                modelVo.setKey(model.getKey());
                modelVo.setName(model.getName());
                modelVo.setCategory(model.getCategory());
                ModelMetaInfoForm metaInfo = JSONUtil.toBean(model.getMetaInfo(), ModelMetaInfoForm.class);
                modelVo.setDescription(metaInfo.getDescription());
                modelVo.setUserFetchUrl(metaInfo.getUserFetchUrl());
                modelVo.setTaskCallbackUrl(metaInfo.getTaskCallbackUrl());
                return modelVo;
            } catch (Exception e) {
                // 如果在获取模型编辑源时发生异常，则记录错误日志
                log.error("Error retrieving model editor source for model ID: {}. Error: {}", id, e.getMessage(), e);
                throw new ServiceException(e.getMessage());
            }
        } else {
            // 如果模型不存在，则记录警告日志
            log.warn("Model not found for model ID: {}", id);
        }
        return modelVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean modelDeploy(String id) {
        try {
            // 查询流程定义模型xml
            byte[] xmlBytes = repositoryService.getModelEditorSource(id);
            if (ArrayUtil.isEmpty(xmlBytes)) {
                throw new ServiceException("模型数据为空，请先设计流程定义模型，再进行部署！");
            }
            BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xmlBytes);
            validateProcess(bpmnModel);
            // 获取流程定义的申请人节点
            //UserTask applyUserTask = ModelUtils.getApplyUserTask(bpmnModel);
            //自动完成第一个任务
            //ModelUtils.addTaskListener(applyUserTask, AutoCompleteFirstTaskListener.class);
            //模型前置处理
            ModelUtils.preDeploy(bpmnModel);
            // 查询模型的基本信息
            Model model = repositoryService.getModel(id);
            // 调用部署相关的api方法进行部署流程定义
            Deployment deployment = repositoryService.createDeployment()
                    // 部署名称
                    .name(model.getName())
                    // 部署标识key
                    .key(model.getKey())
                    // 部署流程分类
                    .category(model.getCategory())
                    // bpmn20.xml资源
                    .addBpmnModel(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnModel)
                    // 租户id
                    .tenantId(model.getTenantId())
                    // 禁用 XML Schema 验证，因为有自定义的属性
                    .disableSchemaValidation()
                    .deploy();
            // 设置 ProcessDefinition 的 category 分类
            ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
            repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
            // 将老的流程定义进行挂起。也就是说，只有最新部署的流程定义，才可以发起任务。
            definitionService.suspendOldVersionProcessDefinition(model.getDeploymentId());
            // 更新 部署id 到流程定义模型数据表中
            model.setDeploymentId(deployment.getId());
            // 更新 版本号
            model.setVersion(definition.getVersion());
            repositoryService.saveModel(model);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public boolean modelDelete(String id) {
        repositoryService.deleteModel(id);
        return true;
    }

    @Override
    public void validateProcess(BpmnModel bpmnModel) {
        List<ValidationError> validationErrors = repositoryService.validateProcess(bpmnModel);
        if (CollUtil.isNotEmpty(validationErrors)) {
            String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(","));
            throw new ServiceException(errorMsg);
        }
    }

}
